package com.rtssoft.scalatest

import scala.collection.mutable.HashMap
import java.io.BufferedOutputStream
import java.io.BufferedReader
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.FileReader
import java.io.OutputStream
import java.io.PrintWriter
import scala.collection.Set
import scala.io.BufferedSource
import scala.throws


class Node(var left: Node, var right: Node, val name: String) {
	
	def this(name: String) = this(null, null, name)
	
	def isLeaf: Boolean = (left==null && right==null)
	
	def ~> (aThat: Node) = { aThat.left = this; aThat }
	
	def <~ (aThat: Node) = { right = aThat; this }
	
	private def iCount(n: Node): Int =
		if (n==null) 0 else if (n.isLeaf) 1 else n.childrenCount
			
    def childrenCount: Int = {
        1 + iCount(left) + iCount(right)
    }
			
    override def toString() = {
        def px(x: Node): String = { if (x==null) " -" else " " + x.toString }
        val r = name + (if (isLeaf) "" else px(left) + "," + px(right))
        if (!isLeaf)
            "(" + r + ")"
        else r
    }
}

class FileWrapper(val name:String) {
    val f:File = new File(name)

    def this(xf:File) = this(xf.getAbsolutePath)

    private[this] def checkFileExistence() = {
        if(!f.exists) {
            if(!f.createNewFile)
                throw new IllegalArgumentException("Can't create file " + name)
        }
    }


    def <-- (s:String): Unit = {
        checkFileExistence
        new PrintWriter(f).append(s).close
    }

    def --> (): String = {
        checkFileExistence
        val r:BufferedSource = new BufferedSource(new FileInputStream(f))
        (for { s <- r.getLines
              z0 = if(!r.hasNext) { r.close(); }
            } yield s).mkString("\n")
    }
}

object FileWrapper {
    implicit def fileBridge(f:File):FileWrapper = new FileWrapper(f)
}



object Main {
	
	implicit def node2wrapper(s: String) = new Node(s)


    /**
     * Finds indexes of all elements are equal to given symbol
     */
    def findIndexes(r: String, c: Char): Array[Int] = {
        (for(x <- 0 until r.length) yield
            if (r.charAt(x) != c) -1 else x
        ).filter(_>=0).toArray
    }


    /**
     * Tries to find all nodes which has more than 1 parent
     */
    def findMultiParentedNodes(r : Node): Set[(Node,Int)] = {
        val m: HashMap[Node,Int] = new HashMap

        def recFunct(x: Node): Unit = {
            if (x != null) {
                m.put(x, m.getOrElseUpdate(x, 0) + 1)
                recFunct(x.left)
                recFunct(x.right)
            }
        }

        recFunct(r);
        for (k <- m.keySet if m.get(k).get > 1)
            yield (k, m.get(k).get)
    }


    /**
     * Reads file to string
     */
    def readFile(name: String): Unit = {
        import FileWrapper._  // how to import implicit definitions
        println(new File(name) -->)
    }


	def main(args: Array[String]): Unit = {
        
        // timeseries :) test
        TStest.tsTest
        //if(true)
        //    return
        
		val root = new Node("O")
        val l1 = new Node("L1")
        val r2 = new Node("R2")
        val r3 = new Node("R3")

		l1 ~> root <~ ("L2" ~> "R1" <~ (l1 ~> r2 <~ (l1 ~> r3)))

//        for(x <- findMultiParentedNodes(root))
//            println("\tNode " + x._1.name + " has " + x._2 + " parents")

        // more succinct code for expression above
        findMultiParentedNodes(root).foreach(x => println("\tNode " + x._1.name + " has " + x._2 + " parents"))

		
		println("Tree output: " + root.toString + "\nnumber of nodes in the tree: " + root.childrenCount)
        
        TestSomeImplicits.test(20)
        TestSomeImplicits.derivExample

        println("Testing my sorter ...")
        SortTest.sortq(List(5, 10, 1, 4, 50).toArray) foreach(x => print(x.toString + " "))

        println("\nReversed order ...")
        SortTest.sortq(List(5, 10, 1, 4, 50).toArray,-1) foreach(x => print(x.toString + " "))

        println()

        def token(t: String): String = {
            val l = t dropWhile(_!='(') takeWhile(_!=')')
            val r = t dropWhile(_!='(') drop(l.length)
            if(l.isEmpty && r.isEmpty)
                return t.trim

            val z : Array[Int] = findIndexes(r, ')')
            (if (z.isEmpty)
                l + r
             else
                 (l + r.substring(0, z(l.count(_=='(') - 1) + 1))
            ) stripPrefix("(") stripSuffix(")") trim
        }

        val x = token(" (O (L1 x, y), (R1 L2, (R2 L1, (R3 L1, -))))")

        val name = x.takeWhile(_!=' ')
        val rest = x.dropWhile(_!=' ').trim
        println("tkn = " + x + " name = " + name + " rest = " + rest)
//        println(token(" x y ( B (C),  (D) ), (E) )"))
//        println(token(" (s) "))

//        def node(cont: String): Unit = {
//            val idxL = cont.findIndexOf(_=='(')
//
//            val s : String = cont.substring(idxL + 1).trim
//            val name= s.split("\\ +")(0).takeWhile(_!=')')
//            val rest2 = s.dropWhile(_!=' ').trim
//
//            if(s.indexOf(')') <= 0) {
//                println("Final Node = " + name)
//                return
//            }
//
//            println("Node = " + name + " rest = " + rest2)
//
//            node(rest2)
//        }
//
//        node(str)

                    // reads file here
                    //readFile(System.getProperty("user.home") + "/.bashrc")
                }

}
